// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.

// Package knmi provides commands to do current weather lookups.
package knmi

import (
	"encoding/xml"
	"io/ioutil"
	"log"
	"net/url"
	"path/filepath"
	"sync"
	"time"

	"notabug.org/mouz/bot/app/util"
	"notabug.org/mouz/bot/ftp"
	"notabug.org/mouz/bot/irc"
	"notabug.org/mouz/bot/irc/cmd"
	"notabug.org/mouz/bot/irc/proto"
	"notabug.org/mouz/bot/plugins"
)

func init() { plugins.Register(&plugin{}) }

type plugin struct {
	m      sync.Mutex
	cmd    *cmd.Set
	config struct {
		ForecastURL string
	}
}

// Load initializes the module and loads any internal resources
// which may be required.
func (p *plugin) Load(prof irc.Profile) error {
	p.cmd = cmd.New(prof.CommandPrefix(), nil)
	p.cmd.Bind(TextForecastName, false, p.cmdForecast)
	file := filepath.Join(prof.Root(), "weather.cfg")
	return util.ReadFile(file, &p.config, false)
}

// Unload cleans the module and unloads any internal resources.
func (p *plugin) Unload(prof irc.Profile) error {
	p.config.ForecastURL = ""
	return nil
}

// Dispatch sends the given, incoming IRC message to the plugin for
// processing as it sees fit.
func (p *plugin) Dispatch(w irc.ResponseWriter, r *irc.Request) {

	if len(p.config.ForecastURL) == 0 {
		log.Println("[knmi] ForecastURL not in weather.cfg")
		return
	}

	p.cmd.Dispatch(w, r)

}

// cmdForecast yields the weather forecast.
func (p *plugin) cmdForecast(w irc.ResponseWriter, r *irc.Request, params cmd.ParamList) {
	p.m.Lock()
	defer p.m.Unlock()

	data, err := p.fetch(p.config.ForecastURL)
	if err != nil {
		log.Println("[knmi]", err)
		return
	}

	err = p.sendResult(w, r, data)
	if err != nil {
		log.Println("[knmi]", err)
		return
	}

}

// fetch fetches the given URL using the FTP protocol.
func (p *plugin) fetch(furl string) ([]byte, error) {

	u, err := url.Parse(furl)
	if err != nil {
		return nil, err
	}

	conn, err := ftp.DialTimeout(u.Host+":21", 5*time.Second)
	if err != nil {
		return nil, err
	}

	err = conn.Login("anonymous", "anonymous")
	if err != nil {
		return nil, err
	}

	resp, err := conn.Retr(u.Path)
	if err != nil {
		return nil, err
	}

	buf, err := ioutil.ReadAll(resp)
	if err != nil {
		return nil, err
	}

	return buf, nil
}

// sendResult formats the result end sends it back to the user.
func (p *plugin) sendResult(w irc.ResponseWriter, r *irc.Request, data []byte) error {

	forecast := knmiForecast{}
	err := xml.Unmarshal(data, &forecast)
	if err != nil {
		return err
	}

	report := forecast.Data.Location.Block[1].FieldContent
	return proto.PrivMsg(w, r.Target, report)

}

// knmiForecast represents the response when the request for the forecast was
// succesful.
//
// The struct is generated using https://github.com/miku/zek via
// https://www.onlinetool.io/xmltogo/ using as input a copy of
// ftp://ftp.knmi.nl/pub_weerberichten/basisverwachting.xml

type knmiForecast struct {
	XMLName  xml.Name `xml:"report"`
	Text     string   `xml:",chardata"`
	Metadata struct {
		Text       string `xml:",chardata"`
		ReportInfo struct {
			Text                        string `xml:",chardata"`
			ReportID                    string `xml:"report_id"`
			ReportLanguage              string `xml:"report_language"`
			ReportTimeCoordinate        string `xml:"report_time_coordinate"`
			ReportDtgIssued             string `xml:"report_dtg_issued"`
			ReportStartValidTime        string `xml:"report_start_valid_time"`
			ReportEndValidTime          string `xml:"report_end_valid_time"`
			ReportIssuedBy              string `xml:"report_issued_by"`
			ReportForecasterID          string `xml:"report_forecaster_id"`
			ReportAdditionalInformation string `xml:"report_additional_information"`
			ReportDisclaimer            string `xml:"report_disclaimer"`
			ReportCredit                string `xml:"report_credit"`
			ReportCreditLogo            string `xml:"report_credit_logo"`
		} `xml:"report_info"`
		ReportLocations struct {
			Text           string `xml:",chardata"`
			ReportLocation struct {
				Text          string `xml:",chardata"`
				LocationID    string `xml:"location_id"`
				LocationDescr string `xml:"location_descr"`
			} `xml:"report_location"`
		} `xml:"report_locations"`
		ReportValidPeriods struct {
			Text              string `xml:",chardata"`
			ReportValidPeriod []struct {
				Text       string `xml:",chardata"`
				ValidID    string `xml:"valid_id"`
				ValidStart string `xml:"valid_start"`
				ValidEnd   string `xml:"valid_end"`
				ValidDescr string `xml:"valid_descr"`
			} `xml:"report_valid_period"`
		} `xml:"report_valid_periods"`
		ReportFields struct {
			Text        string `xml:",chardata"`
			ReportField []struct {
				Text       string `xml:",chardata"`
				FieldID    string `xml:"field_id"`
				FieldDescr string `xml:"field_descr"`
			} `xml:"report_field"`
		} `xml:"report_fields"`
	} `xml:"metadata"`
	Data struct {
		Text     string `xml:",chardata"`
		Location struct {
			Text       string `xml:",chardata"`
			LocationID string `xml:"location_id"`
			Block      []struct {
				Text         string `xml:",chardata"`
				FieldID      string `xml:"field_id"`
				ValidID      string `xml:"valid_id"`
				FieldContent string `xml:"field_content"`
			} `xml:"block"`
		} `xml:"location"`
	} `xml:"data"`
}
